package br.com.unirio.formula1.servico;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import br.com.unirio.formula1.modelo.Campeonato;
import br.com.unirio.formula1.modelo.Corrida;
import br.com.unirio.formula1.modelo.Equipe;
import br.com.unirio.formula1.modelo.Piloto;
import br.com.unirio.formula1.modelo.Resultado;
import br.com.unirio.formula1.modelo.fabrica.FabricaCorrida;
import br.com.unirio.formula1.modelo.fabrica.FabricaPiloto;
import br.com.unirio.formula1.modelo.fabrica.FabricaResultado;
import br.com.unirio.formula1.modelo.persistencia.CampeonatoDAO;
import br.com.unirio.formula1.util.exception.ExcecaoDeModelo;
import br.com.unirio.formula1.util.exception.ExcecaoDeServico;

public class ServicoCampeonato
{
    private ServicoCorrida servicoCorrida;
    private ServicoPilotoEquipe servicoPilotoEquipe;
    private ServicoResultado servicoResultado;

    private Campeonato campeonato;
    private CampeonatoDAO campeonatoDAO;
    double vetorPontos[] = { 25, 18, 15, 12, 10, 8, 6, 4, 2, 1 };

    public ServicoCampeonato()
    {
	this.servicoPilotoEquipe = new ServicoPilotoEquipe();
	this.servicoCorrida = new ServicoCorrida();
	this.servicoResultado = new ServicoResultado();
	campeonatoDAO = new CampeonatoDAO();
	campeonato = campeonatoDAO.recuperar();
    }

    /**
     * Metodo que cria equipes e pilotos a partir da leitura de um arquivo texto e os insere no campeonato.
     * Este metodo verifica se o conjunto de pilotos e equipes a ser inserido suprime algum piloto ou equipe ja inserido
     * na aplicacao.
     * Se o conjunto nao suprimir nenhum piloto ou equipe, ele e inserido no campeonato.
     * 
     * @param caminhoArquivo
     * @throws IOException
     * @throws ExcecaoDeModelo
     * @throws ExcecaoDeServico
     */
    public void criarEquipesEPilotosDoArquivoComValidacacao(String caminhoArquivo) throws IOException, ExcecaoDeModelo,
	    ExcecaoDeServico
    {
	final int INDICE_ID_PILOTO = 0;
	final int INDICE_NOME_PILOTO = 1;
	final int INDICE_SOBRENOME_PILOTO = 2;
	final int INDICE_NOME_EQUIPE = 3;

	Set<Piloto> bufferPilotos = new HashSet<Piloto>();
	Set<Equipe> bufferEquipes = new HashSet<Equipe>();

	List<List<String>> dadosPilotoEquipe = servicoPilotoEquipe.obterDadosNoArquivo(caminhoArquivo);
	for (List<String> list : dadosPilotoEquipe)
	{
	    Equipe equipe = campeonato.getEquipePeloNome(list.get(INDICE_NOME_EQUIPE));
	    // se equipe ainda nao foi criada, cria-se a equipe
	    if (equipe == null)
	    {
		equipe = new Equipe(list.get(INDICE_NOME_EQUIPE));
		bufferEquipes.add(equipe);
	    }
	    Piloto novoPiloto = FabricaPiloto.criaPiloto(list.get(INDICE_ID_PILOTO), list.get(INDICE_NOME_PILOTO),
		    list.get(INDICE_SOBRENOME_PILOTO), equipe);
	    bufferPilotos.add(novoPiloto);

	}

	verificaSupressaoPilotoEquipe(bufferPilotos, bufferEquipes);
	campeonato.setEquipes(bufferEquipes);
	campeonato.setPilotos(bufferPilotos);

    }

    private void verificaSupressaoPilotoEquipe(Set<Piloto> bufferPilotos, Set<Equipe> bufferEquipes)
	    throws ExcecaoDeServico
    {
	verificaSupressaoPilotos(bufferPilotos);
	verificaSupressaoEquipes(bufferEquipes);

    }

    private void verificaSupressaoPilotos(Set<Piloto> bufferPilotos) throws ExcecaoDeServico
    {
	if (!bufferPilotos.containsAll(campeonato.getPilotos()))
	    throw new ExcecaoDeServico("ServicoCampeonato.pilotoSuprimido");

    }

    private void verificaSupressaoEquipes(Set<Equipe> bufferEquipes) throws ExcecaoDeServico
    {
	if (!bufferEquipes.containsAll(campeonato.getEquipes()))
	    throw new ExcecaoDeServico("ServicoCampeonato.equipeSuprimida");

    }

    /**
     * Metodo que cria equipes e pilotos a partir da leitura de um arquivo texto e os insere no campeonato sem
     * verificacao de supressao.
     * 
     * @param caminhoArquivo
     * @throws IOException
     * @throws ExcecaoDeModelo
     * @throws ExcecaoDeServico
     */
    public void criarEquipesEPilotosDoArquivo(String caminhoArquivo) throws IOException
    {
	final int INDICE_ID_PILOTO = 0;
	final int INDICE_NOME_PILOTO = 1;
	final int INDICE_SOBRENOME_PILOTO = 2;
	final int INDICE_NOME_EQUIPE = 3;

	this.campeonato.apagarResultados();
	this.campeonato.apagarEquipesEPilotos();

	List<List<String>> dadosPilotoEquipe = servicoPilotoEquipe.obterDadosNoArquivo(caminhoArquivo);
	for (List<String> list : dadosPilotoEquipe)
	{
	    Equipe equipe = campeonato.getEquipePeloNome(list.get(INDICE_NOME_EQUIPE));
	    // se equipe ainda nao foi criada, cria-se a equipe
	    if (equipe == null)
	    {
		equipe = new Equipe(list.get(INDICE_NOME_EQUIPE));
		campeonato.addEquipe(equipe);
	    }
	    Piloto novoPiloto = FabricaPiloto.criaPiloto(list.get(INDICE_ID_PILOTO), list.get(INDICE_NOME_PILOTO),
		    list.get(INDICE_SOBRENOME_PILOTO), equipe);
	    campeonato.addPiloto(novoPiloto);

	}

    }

    /**
     * Metodo que cria corridas a partir da leitura de um arquivo texto.
     * Este metodo verifica se o arquivo de corridas a ser inserido suprime alguma corrida ja inserida no campeonato
     * anteriormente.
     * Se o arquivo nao suprime, o conjunto de corridas e inserido no campeonato.
     * 
     * @param caminhoArquivo
     * @throws IOException
     * @throws ExcecaoDeModelo
     * @throws ExcecaoDeServico
     */
    public void criarCorridasDoArquivoComValidacao(String caminhoArquivo) throws IOException, ExcecaoDeModelo,
	    ExcecaoDeServico
    {

	// 1;GP AUSTRALIA;17/03/2013;58
	final int INDICE_ID_CORRIDA = 0;
	final int INDICE_NOME_CORRIDA = 1;
	final int INDICE_DATA_CORRIDA = 2;
	final int INDICE_VOLTAS_PREVITAS = 3;

	Set<Corrida> bufferCorridas = new HashSet<Corrida>();

	List<List<String>> dadosCorridas = servicoCorrida.obterDadosNoArquivo(caminhoArquivo);
	for (List<String> lista : dadosCorridas)
	{
	    Corrida novaCorrida = FabricaCorrida.criaCorrida(lista.get(INDICE_ID_CORRIDA),
		    lista.get(INDICE_NOME_CORRIDA), lista.get(INDICE_DATA_CORRIDA), lista.get(INDICE_VOLTAS_PREVITAS));
	    bufferCorridas.add(novaCorrida);
	}
	verificaSupressaoCorrida(bufferCorridas);
	campeonato.setCorridas(bufferCorridas);
    }

    private void verificaSupressaoCorrida(Set<Corrida> bufferCorridas) throws ExcecaoDeServico
    {
	if (!bufferCorridas.containsAll(campeonato.getCorridas()))
	    throw new ExcecaoDeServico("ServicoCampeonato.corridaSuprimida");

    }

    /**
     * Metodo que cria corridas a partir da leitura de um arquivo texto e os insere no campeonato, sem verificar
     * supressoes.
     * 
     * @param caminhoArquivo
     * @throws IOException
     * @throws ExcecaoDeModelo
     */
    public void criarCorridasDoArquivo(String caminhoArquivo) throws IOException, ExcecaoDeModelo
    {

	// 1;GP AUSTRALIA;17/03/2013;58
	final int INDICE_ID_CORRIDA = 0;
	final int INDICE_NOME_CORRIDA = 1;
	final int INDICE_DATA_CORRIDA = 2;
	final int INDICE_VOLTAS_PREVITAS = 3;

	this.campeonato.apagarResultados();
	this.campeonato.apagarCorridas();

	List<List<String>> dadosCorridas = servicoCorrida.obterDadosNoArquivo(caminhoArquivo);
	for (List<String> lista : dadosCorridas)
	{
	    Corrida novaCorrida = FabricaCorrida.criaCorrida(lista.get(INDICE_ID_CORRIDA),
		    lista.get(INDICE_NOME_CORRIDA), lista.get(INDICE_DATA_CORRIDA), lista.get(INDICE_VOLTAS_PREVITAS));
	    campeonato.addCorrida(novaCorrida);
	}
    }

    public void criarResultadoDoArquivo(String caminhoArquivo) throws IOException, ExcecaoDeModelo
    {
	final int INDICE_ID_CORRIDA_E_VOLTAS_REALIZADAS = 0;
	final int INDICE_ID_CORRIDA = 0;
	final int INDICE_VOLTAS_REALIZADAS = 1;
	final int INDICE_INICIO_COLOCACOES = 1;
	final int INDICE_ID_PILOTO = 1;
	final int INDICE_POSICAO = 0;

	List<List<String>> dadosResultado = servicoResultado.obterDadosNoArquivo(caminhoArquivo);

	int idCorrida = Integer.parseInt(dadosResultado.get(INDICE_ID_CORRIDA_E_VOLTAS_REALIZADAS).get(
		INDICE_ID_CORRIDA));
	Corrida corrida = campeonato.getCorridaById(idCorrida);
	validarCorrida(corrida);
	String voltasRealizadas = dadosResultado.get(INDICE_ID_CORRIDA_E_VOLTAS_REALIZADAS).get(
		INDICE_VOLTAS_REALIZADAS);

	// sublista contendo apenas a parte das posicoes do resultado
	List<List<String>> dadosPosicoesResultado = dadosResultado.subList(INDICE_INICIO_COLOCACOES,
		dadosResultado.size());

	// mapeamento posicao - piloto
	HashMap<Integer, Piloto> colocacoes = new HashMap<Integer, Piloto>();

	// populando o mapeamento
	for (List<String> lista : dadosPosicoesResultado)
	{
	    int idPiloto = Integer.parseInt(lista.get(INDICE_ID_PILOTO));
	    Piloto piloto = campeonato.getPilotoById(idPiloto);
	    validarPiloto(piloto);
	    int posicao = Integer.parseInt(lista.get(INDICE_POSICAO));
	    colocacoes.put(posicao, piloto);
	}

	Resultado resultado = FabricaResultado.criaResultado(corrida, voltasRealizadas, colocacoes);
	campeonato.addResultado(resultado);

    }

    public void validarCorrida(Corrida corrida) throws ExcecaoDeModelo
    {
	if (corrida == null)
	    throw new ExcecaoDeModelo("ControleCampeonato.corridaNaoExiste");

    }

    public void validarPiloto(Piloto piloto) throws ExcecaoDeModelo
    {
	if (piloto == null)
	    throw new ExcecaoDeModelo("ControleCampeonato.pilotoNaoExiste");
    }

    public Campeonato getCampeonato()
    {
	return this.campeonato;
    }

    public void setCampeonato(Campeonato campeonato)
    {
	this.campeonato = campeonato;
    }

    public void persistir()
    {
	if (campeonatoDAO.jaFoiSerializado())
	{
	    campeonatoDAO.atualizar(campeonato);
	}
	else
	{
	    campeonatoDAO.criar(campeonato);
	}
    }

    public void apagarResultados()
    {
	campeonato.apagarResultados();
    }
}
